/*
 *    This program is free software; you can redistribute it and/or modify
 *    it under the terms of the GNU General Public License as published by
 *    the Free Software Foundation; either version 2 of the License, or
 *    (at your option) any later version.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU General Public License for more details.
 *
 *    You should have received a copy of the GNU General Public License
 *    along with this program; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 *    2010 year
 *    Author: Michael Khromov aka MikeDM (Mike D'Morto)
 *    E-Mail: mikedm@list.ru
 */


#include <qdebug.h>
#include <QDateTime>
#include <QUrl>
#include <QCryptographicHash>
#include "mcore.h"
#include "def.h"

#if defined(Q_OS_WIN32)
extern Q_CORE_EXPORT int qt_ntfs_permission_lookup;
#endif
mcore::mcore(QString apppath, QObject *parent) :
    QObject(parent)
{
    this->apath= apppath;
    this->lock=false;
    this->stage=0;

    connect(&this->timertik,SIGNAL(timeout()),SLOT(slot_tik()));
    connect(&this->pro,SIGNAL(finished(int)),SLOT(slot_pfin(int)));



}

void mcore::start(){
    //load config
    if(!this->cfg.loadConfig(this->apath+"/"+d_appname+".cfg")){
        applog.slot_print(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss")
                          +" - error load config file. exit");
        exit(2);
    }
    // create log.
    this->applog.setlogfile(this->apath+"/"+cfg.log_dir+"/tbn-agent.log");
    // put log - start
    applog.slot_print(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss")
                      +" - start tbn-agent version "+d_version_major
                      +"."+d_version_minor+" build "+d_version_build);

    // create get url
    if(this->cfg.proto == "http"){
        this->crt="";
    }
    if(this->cfg.proto == "https"){
        if(this->cfg.ignoressl){
            this->crt="-k";
        }else{
            this->crt="--cacert \""+this->apath+"/server.crt\"";
        }
    }

    // timer tik tak
    this->stage=0;
    this->timertik.start(cfg.tik*1000);
}

void mcore::slot_tik(){
    // timer event
    //qDebug()<<"TIK";
    if(!this->lock){
    this->lock=true;

    this->stage=1; // start
    applog.slot_print(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss")
                      +" - start get task");
    //qDebug()<<"exec = "<<"\""+this->apath+curl+"\" "+this->crt+"\""+cfg.proto+"://"+cfg.server
    //        +cfg.s_path+"?act=get&login="+cfg.login
    //        +"&pass="+cfg.password+"&name="+cfg.botname+"\"";
    this->pro.start("\""+this->apath+curl+"\" "+this->crt+" \""+cfg.proto+"://"+cfg.server
                    +cfg.s_path+"?act=get&login="+cfg.login
                    +"&pass="+cfg.password+"&name="+cfg.botname+"\"");

        if (!pro.waitForStarted()){
            applog.slot_print(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss")
                              +" - error start get task: <"+pro.errorString()+">");
        }
    }
    applog.slot_print(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss")+" - TIK locked");


}

void mcore::slot_pfin(int err){
    switch (this->stage){
    case 1:
    // stage 1 finish - get task
    //qDebug()<<"Finish stage 1";
    //test error
        if(err != 0){
            // error
            applog.slot_print(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss")
                              +" - failure get task - error code curl = <"+QString::number(err)+">" );
            this->stage=0;
            this->lock=false;
        }else{
            // all good -  parse
            // read first string.
            applog.slot_print(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss")
                              +" - success get task" );

            QString aa = this->pro.readLine();
            if(aa.mid(0,2) == "ok"){
                // all good
                this->id_task=this->pro.readLine();
                this->id_task.remove("\n");
                this->sname=this->pro.readLine();
                this->sname.remove("\n");
                this->spath=this->pro.readLine();
                this->spath.remove("\n");
                this->serverhash=this->pro.readLine();
                this->serverhash.remove("\n");
                bool testpar=false;
                if(id_task==""){
                   applog.slot_print(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss")+
                                     " - failure task parse - id_task is null");
                   testpar=true;
                }
                if(!(id_task.toInt())){
                   applog.slot_print(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss")+
                                     " - failure task parse - id_task isn't digit");
                   testpar=true;
                }
                if(sname==""){
                   applog.slot_print(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss")+
                                     " - failure task parse - script name is null");
                   testpar=true;
                }
                if(spath==""){
                   applog.slot_print(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss")+
                                     " - failure task parse - script path is null");
                   testpar=true;
                }
                // test server key
                if(this->serverhash==""){
                    applog.slot_print(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss")+
                                      " - failure task parse - script server hash is null");
                    testpar=true;
                }else{
                // generate local key
                    QByteArray aa,bb;
                    aa.append(QString("%1%2").arg(cfg.serverkey).arg(cfg.botname));
                    bb = QCryptographicHash::hash(aa,QCryptographicHash::Md5);
                    QString localhash;
                    localhash.append(bb.toHex());
                    //serverhash.remove("\n");

                    //test hashes
                    if(localhash == this->serverhash){
                        applog.slot_print(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss")+
                                          " - success server hash");
                    }else{
                        applog.slot_print(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss")+
                                          " - failure server hash");
                        testpar=true;
                    }
                }
                if(testpar){
                    // error parse
                    this->stage=0;
                    this->lock=false;
                }else{
                    // parse ok.
                        if(!QFile::exists(cfg.cache_dir+"/"+this->sname) or cfg.ignorecache){
                            // file not found, get it from link
                            this->stage=2;
                            applog.slot_print(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss")+
                                              " - start download script <"+sname+"> from <"+spath+">");
                            //qDebug()<<"exec = "<<this->apath+curl+" "+this->crt+" -o "+this->apath+cfg.cache_dir+"/"+sname+" \""+spath+"\"";
                            this->stage=2;
                            this->pro.start("\""+this->apath+curl+"\" "+this->crt+
                                            " -o \""+this->apath+"/"+cfg.cache_dir+"/"+sname+"\" \""+spath+"\"");
                            if (!pro.waitForStarted()){
                                applog.slot_print(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss")
                                                  +" - error start download script: <"+pro.errorString()+">");
                            }

                        }else{
                            // set status
                            applog.slot_print(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss")
                                              +" - start set status <on work> to script <"+this->sname+">");
                            this->stage=3;
                            //qDebug()<<"exec = "<<this->apath+curl+" "+this->crt+" "+cfg.proto+"://"+cfg.server
                            //        +cfg.s_path+"?act=status&login="+cfg.login
                            //        +"&pass="+cfg.password+"&name="+cfg.botname+"&task="+this->id_task+"&status=1";
                            this->pro.start("\""+this->apath+curl+"\" "+this->crt+" \""+cfg.proto+"://"+cfg.server
                                            +cfg.s_path+"?act=status&login="+cfg.login
                                            +"&pass="+cfg.password+"&name="+cfg.botname+"&task="+this->id_task+"&status=1\"");
                            if (!pro.waitForStarted()){
                                applog.slot_print(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss")
                                                  +" - error set status <on work> : <"+pro.errorString()+">");
                            }
                        }
                }

            }else{
                // fail. parse err or null
                if(aa.mid(0,4) == "null"){
                    applog.slot_print(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss")
                                      +" - task null" );
                }else{
                    if(aa.mid(0,4) == "err:"){
                        applog.slot_print(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss")
                                          +" - failure get task - <"+aa+">" );
                    }else{
                        applog.slot_print(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss")
                                          +" - failure get task - server not correct" );
                    }
                }
                // unlock tik
                this->stage=0;
                this->lock=false;
            }
        }
        break;
    case 2:
        // stage 2 is finish - download script
        //qDebug()<<"Finish stage 2";
        if(err != 0){
            //error
            applog.slot_print(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss")
                              +" - failure download script - error code curl = <"+QString::number(err)+">" );
            this->stage=0;
            this->lock=false;
        }else{
            // all ok
            applog.slot_print(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss")
                              +" - success download script" );
            // set status
            applog.slot_print(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss")
                              +" - start set status <on work> to script <"+this->sname+">");
            this->stage=3;
            //qDebug()<<"exec = "<<this->apath+curl+" "+this->crt+" "+cfg.proto+"://"+cfg.server
            //        +cfg.s_path+"?act=status&login="+cfg.login
            //        +"&pass="+cfg.password+"&name="+cfg.botname+"&task="+this->id_task+"&status=1";
            this->pro.start("\""+this->apath+curl+"\" "+this->crt+" \""+cfg.proto+"://"+cfg.server
                            +cfg.s_path+"?act=status&login="+cfg.login
                            +"&pass="+cfg.password+"&name="+cfg.botname+"&task="+this->id_task+"&status=1\"");
            if (!pro.waitForStarted()){
                applog.slot_print(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss")
                                  +" - error set status <on work> :<"+pro.errorString()+">");
            }
        }

        break;
    case 3:
        // stage 3 - set status - on work
        //qDebug()<<"Finish stage 3";
        if(err != 0){
            //error
            applog.slot_print(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss")
                              +" - failure set status <on work> to script <"+this->sname+"> - error code curl = <"+QString::number(err)+">" );
            this->stage=0;
            this->lock=false;
        }else{
            // all ok
            QString aa=pro.readLine();
            if(aa.mid(0,2) != "ok"){
                //error
                if(aa.mid(0,4) == "err:"){
                    applog.slot_print(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss")
                                      +" - failure set status <on work> to script <"+this->sname+">" );
                }else{
                    applog.slot_print(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss")
                                      +" - failure set status <on work> to script <"+this->sname+"> - server not correct" );
                }

                // unlock tik
                this->stage=0;
                this->lock=false;

            }else{
                //all ok

            applog.slot_print(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss")
                              +" - success set status <on work> to script <"+this->sname+">" );
            // execute script
            this->stage=4;
            applog.slot_print(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss")
                              +" - start exec script <"+this->sname+">" );
            //qDebug()<<"exec = "<<this->apath+"/"+cfg.cache_dir+"/"+this->sname+" >> "+this->apath+"/"+cfg.log_dir+"/"+this->sname+".log";
            this->pro.start("\""+this->apath+"/"+cfg.cache_dir+"/"+this->sname+
                            "\" >> \""+this->apath+"/"+cfg.log_dir+"/"+this->sname+".log\"");
            if (!pro.waitForStarted()){
                // error exec script
                applog.slot_print(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss")+
                                  " - failed exec script <"+this->sname+">");
                this->stage=5;
                this->pro.start("\""+this->apath+curl+"\" "+this->crt+" \""+cfg.proto+"://"+cfg.server
                             +cfg.s_path+"?act=status&login="+cfg.login
                             +"&pass="+cfg.password+"&name="+cfg.botname+"&task="+this->id_task+"&status=3\"");
                if (!pro.waitForStarted()){
                    applog.slot_print(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss")
                                      +" - error set status fail : <"+pro.errorString()+">");
                }

                }else{
                // all ok.
                    applog.slot_print(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss")+
                                      " - success exec script <"+this->sname+">");
                }
            }

        }
        break;
    case 4:
        // stage 4 - execute script
        //qDebug()<<"Finish stage 4";
        //qDebug()<<"ERR="<<err<<"EXITCode="<<this->pro.exitCode()<<"EXITstatus"<<this->pro.exitStatus();

        applog.slot_print(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss")
                          +" - script <"+this->sname+"> return <"+QString::number(err)+">" );


        if( err != 0){
            // set status postexec
            applog.slot_print(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss")
                              +" - start set status <failure> to script <"+this->sname+">");
            this->stage=5;
            //qDebug()<<"exec = "<<this->apath+curl+" "+this->crt+" "+cfg.proto+"://"+cfg.server
            //        +cfg.s_path+"?act=status&login="+cfg.login
            //        +"&pass="+cfg.password+"&name="+cfg.botname+"&task="+this->id_task+"&status=3";
            this->pro.start("\""+this->apath+curl+"\" "+this->crt+" \""+cfg.proto+"://"+cfg.server
                            +cfg.s_path+"?act=status&login="+cfg.login
                            +"&pass="+cfg.password+"&name="+cfg.botname+"&task="+this->id_task+"&status=3\"");
            if (!pro.waitForStarted()){
                applog.slot_print(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss")
                                  +" - error set status <failure> : <"+pro.errorString()+">");
            }
        }else{
            applog.slot_print(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss")
                              +" - start set status <success> to script <"+this->sname+">");
            this->stage=5;
            //qDebug()<<"exec = "<<this->apath+curl+" "+this->crt+" "+cfg.proto+"://"+cfg.server
            //        +cfg.s_path+"?act=status&login="+cfg.login
            //        +"&pass="+cfg.password+"&name="+cfg.botname+"&task="+this->id_task+"&status=2";
            this->pro.start("\""+this->apath+curl+"\" "+this->crt+" \""+cfg.proto+"://"+cfg.server
                            +cfg.s_path+"?act=status&login="+cfg.login
                            +"&pass="+cfg.password+"&name="+cfg.botname+"&task="+this->id_task+"&status=2\"");
            if (!pro.waitForStarted()){
                applog.slot_print(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss")
                                  +" - error set status <success> : <"+pro.errorString()+">");
            }
        }

        break;
    case 5:
        // stage 5 - set status - post exec
        //qDebug()<<"Finish stage 5";
        if(err != 0){
            //error
            applog.slot_print(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss")
                              +" - failure set status to script <"+this->sname+"> - error code curl = <"+QString::number(err)+">" );
            this->stage=0;
            this->lock=false;
        }else{
            // all ok
            QString aa=pro.readLine();
            if(aa.mid(0,2) != "ok"){
                //error
                if(aa.mid(0,4) == "err:"){
                    applog.slot_print(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss")
                                      +" - failure set status to script <"+this->sname+">" );
                }else{
                    applog.slot_print(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss")
                                      +" - failure set status script <"+this->sname+"> - server not correct" );
                }

                // unlock tik
                this->stage=0;
                this->lock=false;

            }else{
                //all ok

                applog.slot_print(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss")
                              +" - success set status to script <"+this->sname+">" );
                this->stage=0;
                this->lock=false;
            }

        }
        break;
    default:
        break;
    }
}


